package nachos.userprog;

import nachos.machine.*;
import nachos.threads.*;
import nachos.userprog.*;

import java.util.*;
import java.io.EOFException;

/**
 * Encapsulates the state of a user process that is not contained in its
 * user thread (or threads). This includes its address translation state, a
 * file table, and information about the program being executed.
 *
 * <p>
 * This class is extended by other classes to support additional functionality
 * (such as additional syscalls).
 *
 * @see	nachos.vm.VMProcess
 * @see	nachos.network.NetProcess
 */
public class UserProcess {
    /**
     * Allocate a new process.
     */
    public UserProcess() {
        pid = pidCount++;
		processes.put(pid, this);
        int numPhysPages = Machine.processor().getNumPhysPages();
        pageTable = new TranslationEntry[numPhysPages];

        //Make fd 0 and 1 refer to standard input and 
        //standard output upon process creation
        fileDescriptorArray[0] = "stdin";
		stdIn = UserKernel.console.openForReading();
        fileDescriptorArray[1] = "stdout";
		stdOut = UserKernel.console.openForWriting();

		// null out open file names
		for(int i = 2; i < fileDescriptorArray.length; i++)
		{
			fileDescriptorArray[i] = null;
		}

        //Get a reference to the FileSystem
        fileSystem = Machine.stubFileSystem();
    }

    /**
     * Allocate and return a new process of the correct class. The class name
     * is specified by the <tt>nachos.conf</tt> key
     * <tt>Kernel.processClassName</tt>.
     *
     * @return	a new process of the correct class.
     */
    public static UserProcess newUserProcess() {
	return (UserProcess)Lib.constructObject(Machine.getProcessClassName());
    }

    /**
     * Execute the specified program with the specified arguments. Attempts to
     * load the program, and then forks a thread to run it.
     *
     * @param	name	the name of the file containing the executable.
     * @param	args	the arguments to pass to the executable.
     * @return	<tt>true</tt> if the program was successfully executed.
     */
    public boolean execute(String name, String[] args) {
	if (!load(name, args))
            return false;
	
        thread = new UThread(this);
        thread.setName(name);
        thread.fork();

        return true;
    }

    /**
     * Save the state of this process in preparation for a context switch.
     * Called by <tt>UThread.saveState()</tt>.
     */
    public void saveState() {
    }

    /**
     * Restore the state of this process after a context switch. Called by
     * <tt>UThread.restoreState()</tt>.
     */
    public void restoreState() {
        Machine.processor().setPageTable(pageTable);
    }

    /**
     * Read a null-terminated string from this process's virtual memory. Read
     * at most <tt>maxLength + 1</tt> bytes from the specified address, search
     * for the null terminator, and convert it to a <tt>java.lang.String</tt>,
     * without including the null terminator. If no null terminator is found,
     * returns <tt>null</tt>.
     *
     * @param	vaddr	the starting virtual address of the null-terminated
     *			string.
     * @param	maxLength	the maximum number of characters in the string,
     *				not including the null terminator.
     * @return	the string read, or <tt>null</tt> if no null terminator was
     *		found.
     */
    public String readVirtualMemoryString(int vaddr, int maxLength) {
        Lib.assertTrue(maxLength >= 0);

	byte[] bytes = new byte[maxLength+1];

        int bytesRead = readVirtualMemory(vaddr, bytes);

	for (int length=0; length<bytesRead; length++) {
	    if (bytes[length] == 0)
                return new String(bytes, 0, length);
            }

        return null;
    }

    /**
     * Transfer data from this process's virtual memory to all of the specified
     * array. Same as <tt>readVirtualMemory(vaddr, data, 0, data.length)</tt>.
     *
     * @param	vaddr	the first byte of virtual memory to read.
     * @param	data	the array where the data will be stored.
     * @return	the number of bytes successfully transferred.
     */
    public int readVirtualMemory(int vaddr, byte[] data) {
        return readVirtualMemory(vaddr, data, 0, data.length);
    }

    /**
     * Transfer data from this process's virtual memory to the specified array.
     * This method handles address translation details. This method must
     * <i>not</i> destroy the current process if an error occurs, but instead
     * should return the number of bytes successfully copied (or zero if no
     * data could be copied).
     *
     * @param	vaddr	the first byte of virtual memory to read.
     * @param	data	the array where the data will be stored.
     * @param	offset	the first byte to write in the array.
     * @param	length	the number of bytes to transfer from virtual memory to
     *			the array.
     * @return	the number of bytes successfully transferred.
     */
    public int readVirtualMemory(int vaddr, byte[] data, int offset,
            int length) {
		Lib.assertTrue(offset >= 0 && length >= 0 && offset+length <= data.length);

        byte[] memory = Machine.processor().getMemory();

		int amount = Math.min(length, memory.length-vaddr);

		for(int i = 0; i < amount; i++)
        {
            int vpn = Processor.pageFromAddress(vaddr+i);
            int voff = Processor.offsetFromAddress(vaddr+i);

            int paddr = Processor.makeAddress(pageTable[vpn].ppn, voff);

            if (paddr < 0 || paddr >= memory.length)
                return 0;

            data[i+offset] = memory[paddr];
        }

        return amount;
    }

    /**
     * Transfer all data from the specified array to this process's virtual
     * memory.
     * Same as <tt>writeVirtualMemory(vaddr, data, 0, data.length)</tt>.
     *
     * @param	vaddr	the first byte of virtual memory to write.
     * @param	data	the array containing the data to transfer.
     * @return	the number of bytes successfully transferred.
     */
    public int writeVirtualMemory(int vaddr, byte[] data) {
        return writeVirtualMemory(vaddr, data, 0, data.length);
    }

    /**
     * Transfer data from the specified array to this process's virtual memory.
     * This method handles address translation details. This method must
     * <i>not</i> destroy the current process if an error occurs, but instead
     * should return the number of bytes successfully copied (or zero if no
     * data could be copied).
     *
     * @param	vaddr	the first byte of virtual memory to write.
     * @param	data	the array containing the data to transfer.
     * @param	offset	the first byte to transfer from the array.
     * @param	length	the number of bytes to transfer from the array to
     *			virtual memory.
     * @return	the number of bytes successfully transferred.
     */
    public int writeVirtualMemory(int vaddr, byte[] data, int offset,
            int length) {
		if(offset < 0 && length < 0 && offset+length < data.length)
		{
			return 0;
		}

        byte[] memory = Machine.processor().getMemory();

		int amount = Math.min(length, memory.length-vaddr);
		
		for(int i = 0; i < amount; i++)
		{
        	int vpn = Processor.pageFromAddress(vaddr+i);
	        int voff = Processor.offsetFromAddress(vaddr+i);

    	    int paddr = Processor.makeAddress(pageTable[vpn].ppn, voff);

			if (paddr < 0 || paddr >= memory.length)
            	return 0;

			memory[paddr] = data[i+offset];
		}

        return amount;
    }

    /**
     * Load the executable with the specified name into this process, and
     * prepare to pass it the specified arguments. Opens the executable, reads
     * its header information, and copies sections and arguments into this
     * process's virtual memory.
     *
     * @param	name	the name of the file containing the executable.
     * @param	args	the arguments to pass to the executable.
     * @return	<tt>true</tt> if the executable was successfully loaded.
     */
    private boolean load(String name, String[] args) {
        Lib.debug(dbgProcess, "UserProcess.load(\"" + name + "\")");
        System.out.println( "UserProcess.load( " + name + " ) ");

        OpenFile executable = fileSystem.open(name, false);
        if (executable == null) {
            Lib.debug(dbgProcess, "\topen failed");
            System.err.println( "Open Failed" );
            return false;
        }

        try {
            coff = new Coff(executable);
	}
	catch (EOFException e) {
            executable.close();
            Lib.debug(dbgProcess, "\tcoff load failed");
            System.err.println( "Coff Load Failed" );
            return false;
        }

        // make sure the sections are contiguous and start at page 0
        numPages = 0;
	for (int s=0; s<coff.getNumSections(); s++) {
            CoffSection section = coff.getSection(s);
            if (section.getFirstVPN() != numPages) {
                coff.close();
                Lib.debug(dbgProcess, "\tfragmented executable");
                return false;
            }
            numPages += section.getLength();
        }

        // make sure the argv array will fit in one page
        byte[][] argv = new byte[args.length][];
        int argsSize = 0;
	for (int i=0; i<args.length; i++) {
            argv[i] = args[i].getBytes();
            // 4 bytes for argv[] pointer; then string plus one for null byte
            argsSize += 4 + argv[i].length + 1;
        }
        if (argsSize > pageSize) {
            coff.close();
            Lib.debug(dbgProcess, "\targuments too long");
            return false;
        }

        // program counter initially points at the program entry point
        initialPC = coff.getEntryPoint();

        // next comes the stack; stack pointer initially points to top of it
        numPages += stackPages;
	initialSP = numPages*pageSize;

        // and finally reserve 1 page for arguments
        numPages++;

	if (!loadSections())
	    return false;

	// store arguments in last page
	int entryOffset = (numPages-1)*pageSize;
	int stringOffset = entryOffset + args.length*4;

        this.argc = args.length;
        this.argv = entryOffset;

	for (int i=0; i<argv.length; i++) {
            byte[] stringOffsetBytes = Lib.bytesFromInt(stringOffset);
	    Lib.assertTrue(writeVirtualMemory(entryOffset,stringOffsetBytes) == 4);
            entryOffset += 4;
            Lib.assertTrue(writeVirtualMemory(stringOffset, argv[i]) ==
                    argv[i].length);
            stringOffset += argv[i].length;
	    Lib.assertTrue(writeVirtualMemory(stringOffset,new byte[] { 0 }) == 1);
            stringOffset += 1;
        }

        return true;
    }

    /**
     * Allocates memory for this process, and loads the COFF sections into
     * memory. If this returns successfully, the process will definitely be
     * run (this is the last step in process initialization that can fail).
     *
     * @return	<tt>true</tt> if the sections were successfully loaded.
     */
    protected boolean loadSections() {
        if (numPages > Machine.processor().getNumPhysPages()) {
            coff.close();
            Lib.debug(dbgProcess, "\tinsufficient physical memory");
            return false;
        }

	// check if there are enough free pages of memory
    if(numPages > UserKernel.freePages.size())
    {
            Lib.debug(dbgProcess, "\tinsufficent free memory");
			coff.close();
            return false;
    }

    // allocate the needed pages
    for(int i = 0; i < numPages; i++)
    {
         int physPage = UserKernel.freePages.removeFirst();
         pageTable[i] = new TranslationEntry(i, physPage, true, false, false, false);
    }

    //set the rest of the page table to invalid
    for(int i = numPages; i < Machine.processor().getNumPhysPages(); i++)
    {
         pageTable[i] = new TranslationEntry(i, 0, false, false, false, false);
    }


    // load sections
	for (int s=0; s<coff.getNumSections(); s++) {
            CoffSection section = coff.getSection(s);

	    Lib.debug(dbgProcess, "\tinitializing " + section.getName()
		      + " section (" + section.getLength() + " pages)");

	    for (int i=0; i<section.getLength(); i++) {
		int vpn = section.getFirstVPN()+i;

                //set the page to be readonly if the section is read only
			if(section.isReadOnly())
				pageTable[vpn].readOnly = true;

        section.loadPage(i, pageTable[vpn].ppn);
            }
        }

		coff.close();
        return true;
    }

    /**
     * Release any resources allocated by <tt>loadSections()</tt>.
     */
    protected void unloadSections() {
 		// free memory
        for(int i = 0; i < numPages; i++)
        {
            if(pageTable[i].valid)
            {
                int freePage = pageTable[i].ppn;
                UserKernel.freePages.addLast(freePage);

                byte[] memory = Machine.processor().getMemory();

				// blank out used pages
                for(int j = 0; j < pageSize; j++)
                {
                    int paddr = Processor.makeAddress(freePage, j);
                    memory[paddr] = 0;
                }
            }
        }

    }

    /**
     * Initialize the processor's registers in preparation for running the
     * program loaded into this process. Set the PC register to point at the
     * start function, set the stack pointer register to point at the top of
     * the stack, set the A0 and A1 registers to argc and argv, respectively,
     * and initialize all other registers to 0.
     */
    public void initRegisters() {
        Processor processor = Machine.processor();

        // by default, everything's 0
	for (int i=0; i<processor.numUserRegisters; i++)
	    processor.writeRegister(i, 0);

	// initialize PC and SP according
        processor.writeRegister(Processor.regPC, initialPC);
        processor.writeRegister(Processor.regSP, initialSP);

        // initialize the first two argument registers to argc and argv
        processor.writeRegister(Processor.regA0, argc);
        processor.writeRegister(Processor.regA1, argv);
    }

    /**
     * Handle the halt() system call. 
     */
    private int handleHalt() {
		if(!rootProcess)
			return -1;
		
        Machine.halt();

        Lib.assertNotReached("Machine.halt() did not halt machine!");
        return 0;
    }

    private int handleExit(int status) {
        boolean intStatus = Machine.interrupt().disable();

		unloadSections();

        // close files
		for(String f : fileDescriptorArray)
		{
			if(f != null)
				removeOpenFile(f);
        }
		
		exitStatus = status;

		processes.remove(pid);

		processJoinSemaphore.V();
		

        // this is the root process exiting so halt the machine
		if(processes.isEmpty())
		{
            System.out.println("last process: halting");
            UserKernel.kernel.terminate();
        }

        // schedule the next thread and never put 
        // this one back in the ready queue

		KThread.finish();

        Machine.interrupt().restore(intStatus);

        return 0;
    }

    private int handleCreate(String name) {

        int fd = -1;
        
        /* If name == null, that means that a filename was not correctly
         ** passed to this syscall.  Therefore, return fd = -1*/
        if (name == null)
            return fd;

		// on deletion list - not allowed to use
		if(deletionList.get(name) != null)
			return fd;

        //Attempt to open the file on disk.
        //If the file does not exist, create it
        OpenFile newFile = fileSystem.open(name, true);

        if (newFile == null) return -1;

        for (int i = 2; i < fileDescriptorArray.length; i++)
		{
            if (fileDescriptorArray[i] == null)
			{
                fileDescriptorArray[i] = name;
                fileReadPos[i] = 0;
                fileWritePos[i] = 0;
				fd = i;
				newFile.close();
                break;
            }
        }
        
        if (fd == -1)
		{
			newFile.close();
			return -1;
		}

        // on success Return the file descriptor
        return fd;
    }

    /**
     * If FD exists for name, return the fd, otherwise return -1
     */
    private int fdExists( String name ) {
       for( int i=2; i<fileDescriptorArray.length; i++ ) {
          if( fileDescriptorArray[i] != null &&
                fileDescriptorArray[i] != null &&
                name.equals( fileDescriptorArray[i] ) ) {
             return i;
                }
       }
       return -1;
    }

    private int handleOpen(String name) {
        int fd = -1;
		
        /* If name == null, that means that a filename was not correctly
         ** passed to this syscall.  Therefore, return fd = -1*/
        if (name == null) {
            return fd;
        }

		if(deletionList.get(name) != null)
		{
				return fd;
		}

        /* If fd already exists, just return it */
        fd = fdExists( name );
        if( fd != -1 ) {
           return fd;
        }

        OpenFile openFile = fileSystem.open(name, false);

        //make sure the file exists and opened
        if (openFile == null) return -1;

        //check for the next available spot in the array
        //and place the file in that spot
        for (int i = 2; i < fileDescriptorArray.length; i++)
		{
            if (fileDescriptorArray[i] == null)
			{
                fileDescriptorArray[i] = name;
                fileWritePos[i] = 0;
                fileReadPos[i] = 0;
				addOpenFile(name);
				fd = i;
				openFile.close();
                break;
            }
        } 
        
		// failed to find free fd
        if(fd == -1)
		{
			openFile.close();
			return -1;
		}

		// on success return the fd
        return fd;
    }

    private int handleRead(int fd, int vaddr, int count) {
        byte[] buffer = new byte[count];

        // error if STDOUT is read
		if(fd == STDOUT || fd < 0 || fd > fileDescriptorArray.length)
            return -1;
        
		// handle STDIN
		if(fd == STDIN)
		{
			int stdRead = stdIn.read(buffer, 0, count);
			if(stdRead == -1) return -1;

			int stdWritten = writeVirtualMemory(vaddr, buffer);
			if(stdWritten < stdRead) return -1;

			return stdRead;
		}

        //Get file position
        int pos = fileReadPos[fd];

		//open file for only as long as we need to
        OpenFile openFile = fileSystem.open( fileDescriptorArray[fd], false );
		openFile.seek(pos);
        int bytesRead = openFile.read(buffer, 0, count);
		pos = openFile.tell();
		openFile.close();

		if(bytesRead == -1) return -1;

        // adjust position by amount successfully written
        fileReadPos[fd] += bytesRead;

       	int bytesWritten = writeVirtualMemory( vaddr, buffer );
        if (bytesWritten < bytesRead)
        	return -1;
        
        return bytesRead;
    }

    private int handleWrite(int fd, int vaddr, int count) {
        byte[] data = new byte[count];

		if(fd == STDIN || fd < 0 || fd > fileDescriptorArray.length)
			return -1;

        if(fd == STDOUT) {
            int stdRead = readVirtualMemory(vaddr, data, 0, count);
			if(stdRead == -1) return -1;

            int stdWrite = stdOut.write(data, 0, count);
			if(stdWrite < count) return -1;

			return stdWrite;
        }
        
        int pos = fileWritePos[fd];
        readVirtualMemory(vaddr, data, 0, count);
	
		OpenFile openFile = fileSystem.open(fileDescriptorArray[fd], false);
		openFile.seek(pos);
        int bytesWritten = openFile.write(pos, data, 0, count);
		pos = openFile.tell();
		openFile.close();

        if (bytesWritten < count)
		{
            return -1;
        }

        fileWritePos[fd] = pos;
        
        return bytesWritten;
    }

    private int handleClose(int fd) {
        if (fd >= 2 && fd < fileDescriptorArray.length)
		{
			removeOpenFile(fileDescriptorArray[fd]);

            fileDescriptorArray[fd] = null;
            fileWritePos[fd] = 0;
            fileReadPos[fd] = 0;
            return 0;
        }

        return -1;
    }

    private int handleUnlink(String name) {

        /** 
         * Delete a file from the file system. If no processes have the file open, the
         * file is deleted immediately and the space it was using is made available for
         * reuse.
         *
         * If any processes still have the file open, the file will remain in existence
         * until the last file descriptor referring to it is closed. However, creat()
         * and open() will not be able to return new file descriptors for the file
         * until it is deleted.
         *
         * Returns 0 on success, or -1 if an error occurred.
         */

        /* If name == null, that means that a filename was not correctly
         ** passed to this syscall.  Therefore, return fd = -1*/
        if (name == null) {
            return -1;
        }
		else
		{
			if(openList.get(name) == null)
			{
				// no open handles so we can delete immediately
				fileSystem.remove(name);
				//System.out.println("Deleting file immediately");
			}
			else
			{
				if(deletionList.get(name) == null)
				{
					//add to the deletionList if not already there
					deletionList.put(name, 1);
					//System.out.println("Putting on deletion list");
				}
			}
			return 0;
		}
    }


    private static final int
        syscallHalt = 0,
	syscallExit = 1,
	syscallExec = 2,
	syscallJoin = 3,
	syscallCreate = 4,
	syscallOpen = 5,
	syscallRead = 6,
	syscallWrite = 7,
	syscallClose = 8,
	syscallUnlink = 9;

    /**
     * Handle a syscall exception. Called by <tt>handleException()</tt>. The
     * <i>syscall</i> argument identifies which syscall the user executed:
     *
     * <table>
     * <tr><td>syscall#</td><td>syscall prototype</td></tr>
     * <tr><td>0</td><td><tt>void halt();</tt></td></tr>
     * <tr><td>1</td><td><tt>void exit(int status);</tt></td></tr>
     * <tr><td>2</td><td><tt>int  exec(char *name, int argc, char **argv);
     * 								</tt></td></tr>
     * <tr><td>3</td><td><tt>int  join(int pid, int *status);</tt></td></tr>
     * <tr><td>4</td><td><tt>int  creat(char *name);</tt></td></tr>
     * <tr><td>5</td><td><tt>int  open(char *name);</tt></td></tr>
     * <tr><td>6</td><td><tt>int  read(int fd, char *buffer, int size);
     *								</tt></td></tr>
     * <tr><td>7</td><td><tt>int  write(int fd, char *buffer, int size);
     *								</tt></td></tr>
     * <tr><td>8</td><td><tt>int  close(int fd);</tt></td></tr>
     * <tr><td>9</td><td><tt>int  unlink(char *name);</tt></td></tr>
     * </table>
     * 
     * @param	syscall	the syscall number.
     * @param	a0	the first syscall argument.
     * @param	a1	the second syscall argument.
     * @param	a2	the third syscall argument.
     * @param	a3	the fourth syscall argument.
     * @return	the value to be returned to the user.
     */
    public int handleSyscall(int syscall, int a0, int a1, int a2, int a3) {
        switch (syscall)
		{
            case syscallHalt:
                return handleHalt();
            case syscallExit:
                return handleExit(a0);
            case syscallExec:
                return handleExec(a0, a1, a2);
			case syscallJoin:
				return handleJoin(a0, a1);
            case syscallCreate:
                return handleCreate(readVirtualMemoryString(a0, Processor.pageSize));
            case syscallOpen:
                return handleOpen(readVirtualMemoryString(a0, Processor.pageSize));
            case syscallRead:
                return handleRead(a0, a1, a2);
            case syscallWrite:
                return handleWrite(a0, a1, a2);
            case syscallClose:
                return handleClose(a0);
            case syscallUnlink:
                return handleUnlink(readVirtualMemoryString(a0, Processor.pageSize));


            default:
                Lib.debug(dbgProcess, "Unknown syscall " + syscall);
				handleExit(-1);
        }

        return 0;
    }

	private int readCharPointer(int vaddr)
	{
        byte[] mem = new byte[4];
        readVirtualMemory(vaddr, mem);

        return Lib.bytesToInt(mem, 0);
    }

    private int handleExec(int vaddrFile, int argc, int vaddrArgv) {
        String file = readVirtualMemoryString(vaddrFile, Processor.pageSize);

		if(file == null || argc < 0)
		{
            return -1;
        }

        String[] args = new String[argc];

		for(int i = 0; i < argc; i++)
		{
            args[i] = readVirtualMemoryString(readCharPointer(vaddrArgv + (i * 4)), Processor.pageSize);

			if(args[i] == null)
                return -1;
            }

        UserProcess p = new UserProcess();

		children.put(p.pid, p);

		if(p.execute(file, args) == false)
		{
			processes.remove(p.pid);
            return -1;
        }

        return p.pid;
    }

	private int handleJoin(int joinPid, int vaddrStatus)
	{
		if(!children.containsKey(joinPid))
		{
			return -1;
		}

		if(!children.containsKey(joinPid))
		{
			return 0;
		}

		children.get(joinPid).processJoinSemaphore.P();

		byte[] statusBytes = Lib.bytesFromInt(children.get(joinPid).exitStatus);
		writeVirtualMemory(vaddrStatus, statusBytes);

		return 1;	
	}

    /**
     * Handle a user exception. Called by
     * <tt>UserKernel.exceptionHandler()</tt>. The
     * <i>cause</i> argument identifies which exception occurred; see the
     * <tt>Processor.exceptionZZZ</tt> constants.
     *
     * @param	cause	the user exception that occurred.
     */
    public void handleException(int cause) {
        Processor processor = Machine.processor();

        switch (cause) {
            case Processor.exceptionSyscall:
                int result = handleSyscall(
                        processor.readRegister(Processor.regV0),
                        processor.readRegister(Processor.regA0),
                        processor.readRegister(Processor.regA1),
                        processor.readRegister(Processor.regA2),
				       processor.readRegister(Processor.regA3)
				       );
                processor.writeRegister(Processor.regV0, result);
                processor.advancePC();
                break;

            default:
                Lib.debug(dbgProcess, "Unexpected exception: " +
                        Processor.exceptionNames[cause]);
                handleExit(-1);
        }
    }

	protected void addOpenFile(String name)
	{
		if(openList.get(name) == null)
		{
			openList.put(name, 1);
		}
		else
		{
			openList.put(name, openList.get(name)+1);
		}
	}

	protected void removeOpenFile(String name)
	{
		if(openList.get(name) == null)
			return;

		openList.put(name, openList.get(name)-1);

		if(openList.get(name) == 0 && deletionList.get(name) != null)
		{
			// file is ready to be deleted
			//System.out.println("Delayed file deletion");
			fileSystem.remove(name);
			deletionList.remove(name);
		}

		if(openList.get(name) == 0)
		{
			// remove from list - no other handles open
			openList.remove(name);
		}
	}


	// global list of files waiting to be deleted
    protected static Hashtable<String,Integer> deletionList = new Hashtable<String,Integer>();

	// global list of files currently open
    protected static Hashtable<String,Integer> openList = new Hashtable<String,Integer>();

    /** FileSystem referred to by this UserProcess */
    protected FileSystem fileSystem = null;
    
    /** The program being run by this process. */
    protected Coff coff;

    /** This process's page table. */
    protected TranslationEntry[] pageTable;
    /** The number of contiguous pages occupied by the program. */
    protected int numPages;

    /** The number of pages in the program's stack. */
    protected final int stackPages = 8;
    
    private int initialPC, initialSP;
    private int argc, argv;
	
    private static final int pageSize = Processor.pageSize;
    private static final char dbgProcess = 'a';
    
    private String[] fileDescriptorArray = new String[18]; //16 + stdin/stdout
    private int[] fileReadPos = new int[18]; //16 + stdin/stdout
    private int[] fileWritePos = new int[18]; //16 + stdin/stdout

	private final int STDIN = 0;
	private final int STDOUT = 1;
	private OpenFile stdIn = null;
	private OpenFile stdOut = null;

	private Hashtable<Integer,UserProcess> children = new Hashtable<Integer,UserProcess>();
	private static Hashtable<Integer,UserProcess> processes = new Hashtable<Integer,UserProcess>();

	// the thread representing the code being run
	private UThread thread = null;

	public Semaphore processJoinSemaphore = new Semaphore(0);

	// pid
	private int pid = -1;
	public int exitStatus = 0;
	public boolean rootProcess = false;

	// pid counter
    private static int pidCount = 0;
    
}
